package erfgame.core.world.terrain.async;

import java.util.ArrayList;
import java.util.concurrent.Executor;

import erfgame.core.dao.ObjectDAO;

public class UnsafeAsyncObjectDAOProxy<KeyType, ObjectType> implements
		ObjectDAO<KeyType, ObjectType> {
	
	private ObjectDAO<KeyType, ObjectType> proxied;
	private ArrayList<Storer> storers;
	private Executor executor;
	
	public UnsafeAsyncObjectDAOProxy( ObjectDAO<KeyType, ObjectType> proxied, Executor executor ){
		this.proxied = proxied;
		this.executor = executor;
		this.storers = new ArrayList<Storer>();
	}

	public boolean containsObject(KeyType key) {
		for( int i=storers.size(); i>0; ) {
			i--;
			Storer storer = storers.get( i );
			if( storer.key.equals( key ) ) {
				return true;
			}
		}
		return this.proxied.containsObject(key);
	}

	public boolean removeObject(KeyType key) {
		boolean removed = this.proxied.removeObject(key);
		for( int i=storers.size(); i>0; ) {
			i--;
			Storer storer = storers.get( i );
			if( storer.key.equals( key ) ) {
				storers.remove(i);
				free(storer.object);
				removed = true;
			}
		}
		return removed;
	}

	public ObjectType retrieveObject(KeyType key) {
		for( int i=storers.size(); i>0; ) {
			i--;
			Storer storer = storers.get( i );
			if( storer.key.equals( key ) ) {
				return assign(storer.object);
			}
		}
		return proxied.retrieveObject(key);
	}

	public void storeObject(KeyType key, ObjectType object) {
		Storer storer = new Storer( key, assign(object), proxied );
		this.storers.add(storer);
		this.executor.execute( storer );
	}
	
	protected ObjectType assign( ObjectType o ) {
		return o;
	}
	
	protected void free( ObjectType o ) {
		
	}
	
	private class Storer implements Runnable {
		
		KeyType key;
		ObjectType object;
		ObjectDAO<KeyType, ObjectType> proxied;
		
		public Storer( KeyType key, ObjectType object, ObjectDAO<KeyType, ObjectType> proxied ) {
			this.key = key;
			this.object = object;
			this.proxied = proxied;
		}
		
		public void run() {
//				System.out.println( "storing "+key );
			if( storers.contains( this ) ) {
				proxied.storeObject(key, object);
				storers.remove( this );
				free(object);
			}
		}

		@Override
		public boolean equals(Object obj) {
			if( obj != null && obj.getClass().equals( this.getClass() ) ) {
				Storer storer = (Storer)obj;
				return storer.key.equals( this.key ) && storer.proxied == this.proxied;
			} else {
				return false;
			}
		}

		@Override
		public int hashCode() {
			return key.hashCode() + proxied.hashCode();
		}
	}
}
